﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using Brjnk.Common;
using Brjnk.CoordinateSystem2D.Layers;
using Brjnk.GpxExplorer.Data;
using Brjnk.GpxExplorer.UI;
using Brjnk.GpxExplorer.ViewModel;

namespace Brjnk.GpxExplorer.Workspace
{
    public class PhotoManager
    {
        public Photos Photos { get; private set; }

        private DataModel data;

        private AreaManager area;

        private PhotosLoader photosLoader;

        /// <param name="area">AreaManager slouží pro zobrazování fotografií v okně (vrstva Photos)</param>
        public PhotoManager(DataModel data, AreaManager area)
        {
            this.data = data;
            this.Photos = data.Photos;
            this.area = area;

            photosLoader = new PhotosLoader(data);

            var defaultView = System.Windows.Data.CollectionViewSource.GetDefaultView(this.Photos.Collection);
            if (defaultView.CanSort)
            {
                defaultView.SortDescriptions.Add(new System.ComponentModel.SortDescription("CorrectedCreationTime", System.ComponentModel.ListSortDirection.Ascending));
            }

            CreateCommnads();
        }

        private void Reload(object items)
        {
            if (items is IList)
                Load((items as IList).Cast<Photo>().Select(p => p.FullName).ToArray());

            else ForEach(items, p => Load(p.FullName));
        }

        private void SetTimeCorrection(object o)
        {
            var win = new TimeCorrectionWindow();
            if (win.ShowDialog() ?? false)
            {
                ForEach(o, p => p.TimeCorrection = win.TimeCorrection);
            }
        }

        private void Remove(object o)
        {
            //BeginUpdate a EndUpdate není voláno, protože třída Photos umí aktualizovat pouze fotografie, ve které došlo ke změně
            //Při volání EndUpdate by naopak došlo k aktualizaci všech fotografií.
            ForEach(o, p => Photos.Collection.Remove(p));
        }

        private void ForEach(object items, Action<Photo> action)
        {
            if (items is IList)
            {
                List<Photo> toProcess = new List<Photo>(); ;
                foreach (Photo item in (items as IList))
                {
                    toProcess.Add(item);
                }

                foreach (var item in toProcess)
                    action(item);
            }
            else if (items is Photo)
            {
                action(items as Photo);
            }
            else System.Diagnostics.Debug.WriteLine("Argument metody je neplatný");
        }

        private void Load()
        {
            var ofd = new Microsoft.Win32.OpenFileDialog();
            ofd.Multiselect = true;
            if (ofd.ShowDialog() ?? false)
            {
                Load(ofd.FileNames);
            }
        }

        private void Load(params string[] fileNames)
        {
            foreach (var path in fileNames)
            {
                photosLoader.Load(path);
            }
        }

        private void ExportCsv()
        {
            var sfd = new Microsoft.Win32.SaveFileDialog();
            if (sfd.ShowDialog() ?? false)
            {
                using (var writer = new CsvWriter(sfd.FileName))
                {
                    writer.WriteHeader("FullName", "FileName", "DateTime", "Width", "Height", "Orientation", "Lat", "Lon", "Elevation", "Track", "NoExifCreationTime", "Ambiguous");
                    foreach (var photo in Photos.Collection)
                    {
                        bool ambiguous = photo.Locations.Count > 1;
                        foreach (var located in photo.Locations)
                        {
                            writer.Write(photo.FullName, photo.FileName,
                                photo.CreationTime.ToString(System.Globalization.CultureInfo.InvariantCulture),
                                photo.PixelWidth, photo.PixelHeight,
                                (int)photo.Orientation,
                                located.Position.LatTotalDegree.ToString(System.Globalization.CultureInfo.InvariantCulture),
                                located.Position.LonTotalDegree.ToString(System.Globalization.CultureInfo.InvariantCulture),
                                (located.Elevation ?? -1).GetDistanceValue(DistanceUnit.Metre).ToString(System.Globalization.CultureInfo.InvariantCulture),
                                located.Locator.Source.Name,
                                photo.FileCreationTimeUsed ? 1 : 0,
                                ambiguous ? 1 : 0);
                        }
                    }
                }
            }
        }

        private void ShowPhotos(IList<Photo> photos)
        {
            area.PhotosLayer.ShowImages(photos.Select(p => new PhotoRef(p, new Point(0, 0))).Cast<IPhoto>().ToList());
        }

        private ManagePhotoWindow managePhotoWindow;

        private void Manage()
        {
            if (managePhotoWindow != null && managePhotoWindow.IsVisible) managePhotoWindow.Focus();
            else
            {
                managePhotoWindow = new ManagePhotoWindow(this);
                managePhotoWindow.Show();
            }
        }

        #region commands

        private void CreateCommnads()
        {
            LoadCommand = new GenericCommand(o => Load());
            ManageCommand = new GenericCommand(o => Manage());
            RemoveCommand = new GenericCommand(o => Remove(o));
            SetTimeCorrectionCommand = new GenericCommand(o => SetTimeCorrection(o));
            ReloadCommand = new GenericCommand(o => Reload(o));
            ExportCsvCommand = new GenericCommand(o => ExportCsv());
            ShowAllCommand = new GenericCommand(o => ShowPhotos(Photos.Collection.ToList()));
            ShowLocatedCommand = new GenericCommand(o => ShowPhotos(Photos.Collection.Where(p => p.Locations.Count > 0).ToList()));
            ShowUnlocatedCommand = new GenericCommand(o => ShowPhotos(Photos.Collection.Where(p => p.Locations.Count == 0).ToList()));
            ShowAmbiguousCommand = new GenericCommand(o => ShowPhotos(Photos.Collection.Where(p => p.Locations.Count > 1).ToList()));
        }

        /// <summary>
        /// Otevře OpenFileDialog pro přidání fotografií
        /// </summary>
        public ICommand LoadCommand { get; private set; }

        /// <summary>
        /// Otevře okno pro správu fotografíí
        /// </summary>
        public ICommand ManageCommand { get; private set; }

        /// <summary>
        /// Otevře okno pro nastavení časové korekce. Parametr tohoto příkazu je kolekce fotografií (typu PhotoUI)
        /// </summary>
        public ICommand SetTimeCorrectionCommand { get; private set; }

        /// <summary>
        /// Provede znovunačtení předaných fotografíí
        /// </summary>
        public ICommand ReloadCommand { get; private set; }

        /// <summary>
        /// Odebere fotografie předané jako parametr (kolekce typu PhotoUI nebo jediný objekt typu PhotoUI či Photo)
        /// </summary>
        public ICommand RemoveCommand { get; private set; }

        /// <summary>
        /// Vyexportuje geolokační informace do csv souboru
        /// </summary>
        public ICommand ExportCsvCommand { get; private set; }

        /// <summary>
        /// Zobrazí všechny fotografie v okně
        /// </summary>
        public ICommand ShowAllCommand { get; private set; }

        /// <summary>
        /// Zobrazí všechny fotografie se souřadnicema
        /// </summary>
        public ICommand ShowLocatedCommand { get; private set; }

        /// <summary>
        /// Zobrazí fotografie bez souřadnice
        /// </summary>
        public ICommand ShowUnlocatedCommand { get; private set; }

        /// <summary>
        /// Zobrazí fotografie s více souřadnicema
        /// </summary>
        public ICommand ShowAmbiguousCommand { get; private set; }

        #endregion commands
    }
}